package com.srtp.secureprotocol.controller

import com.srtp.secureprotocol.main.SecureProtocol
import com.srtp.secureprotocol.serializable.req.CreateSessionRequest
import com.srtp.secureprotocol.serializable.req.DecryptTextRequest
import com.srtp.secureprotocol.serializable.req.EncryptTextRequest
import com.srtp.secureprotocol.serializable.req.InitStickySessionRequest
import com.srtp.secureprotocol.serializable.req.SenderKeyRequest
import com.srtp.secureprotocol.serializable.req.SessionBundle
import com.srtp.secureprotocol.serializable.resp.SenderKey
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.ApplicationCall
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import kotlinx.serialization.Serializable
import org.json.JSONObject

class SessionController {
    @Serializable
    data class ApiResponse<T>(
        val code: Int,
        val message: String,
        val data: T? = null
    )

    suspend fun initPairwiseSession(
        request: SessionBundle,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val bundleJson = JSONObject().apply {
                put("userId", request.userId)
                put("localId", request.localId)
                put("identityKey", request.identityKey)
                put("signedPreKey", request.signedPreKey)
                put("signedPreKeyId", request.signedPreKeyId)
                put("preKeyId", request.preKeyId)
                put("preKey", request.preKey)
                put("signature", request.signature)
            }

            secureProtocol.initPairwiseSession(bundleJson)
            call.respond(
                HttpStatusCode.OK,
                ApiResponse(code = 200, message = "ok", data = null)
            )
        } catch (e: Exception) {
            call.respond(
                HttpStatusCode.BadRequest,
                "Decryption failed: ${e.message ?: "Unknown error"}"
            )
        }
    }

    suspend fun encryptTextPairwise(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, String>>()
            val userId = json["userId"] ?: throw IllegalArgumentException("userId is required")
            val text = json["text"] ?: throw IllegalArgumentException("text is required")

            val encryptedText = secureProtocol.encryptTextPairwise(userId, text)

            call.respond(mapOf("status" to "success", "ciphertext" to encryptedText))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun decryptTextPairwise(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val senderId = json["senderId"] as? String
                ?: throw IllegalArgumentException("senderId is required")
            val isStickyKey = json["isStickyKey"] as? Boolean ?: false
            val cipher =
                json["cipher"] as? String ?: throw IllegalArgumentException("cipher is required")

            val plainText = secureProtocol.decryptTextPairwise(senderId, isStickyKey, cipher)

            call.respond(mapOf("status" to "success", "plaintext" to plainText))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun createStickySession(
        request: CreateSessionRequest,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val userId = request.userId
            val sessionId = request.sessionId

            val result: JSONObject = secureProtocol.createStickySession(userId, sessionId)

            val senderKey = SenderKey(
                id = result.getInt("id"),
                key = result.getString("key")
            )
            call.respond(senderKey)
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun getSenderKey(
        request: SenderKeyRequest,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val senderId = request.senderId
            val targetId = request.targetId
            val sessionId = request.sessionId
            val isSticky = request.isSticky

            val senderKeyBase64 = secureProtocol.getSenderKey(senderId, targetId, sessionId, isSticky)

            call.respond(mapOf("status" to "success", "data" to senderKeyBase64))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun initStickySession(
        request: InitStickySessionRequest,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val senderId = request.senderId
            val sessionId = request.sessionId
            val cipherSenderKey = request.senderKey
            val identityKeyId = request.identityKeyId
            secureProtocol.initStickySession(senderId, sessionId, cipherSenderKey, identityKeyId)
            call.respond(mapOf("status" to "success", "message" to "Sticky session initialized"))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun encryptText(
        request: EncryptTextRequest,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val senderId = request.senderId
            val sessionId = request.sessionId
            val text = request.text
            val isSticky = request.isSticky

            val encrypted = secureProtocol.encryptText(senderId, sessionId, text, isSticky)
            call.respond(mapOf("status" to "success", "cipher" to encrypted))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun decryptText(
        request: DecryptTextRequest,
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val senderId = request.senderId
            val sessionId = request.sessionId
            val cipher =request.cipher
            val isSticky = request.isSticky
            val decrypted = secureProtocol.decryptText(senderId, sessionId, cipher, isSticky)
            call.respond(mapOf("status" to "success", "text" to decrypted))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun sessionExists(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val senderId = json["senderId"] as String
            val stickId = json["stickId"] as String
            val exists = secureProtocol.sessionExists(senderId, stickId)
            call.respond(mapOf("status" to "success", "exists" to exists))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun getChainStep(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val userId = json["userId"] as String
            val stickId = json["stickId"] as String
            val step = secureProtocol.getChainStep(userId, stickId)
            call.respond(mapOf("status" to "success", "step" to step))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun ratchetChain(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val userId = json["userId"] as String
            val stickId = json["stickId"] as String
            val steps = (json["steps"] as? Number)?.toInt() ?: 1

            secureProtocol.ratchetChain(userId, stickId, steps)
            call.respond(mapOf("status" to "success"))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun reinitMyStickySession(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val userId = json["userId"] as String
            val senderKey = JSONObject(json["senderKey"].toString())

            secureProtocol.reinitMyStickySession(userId, senderKey)
            call.respond(mapOf("status" to "success"))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun decryptStickyKey(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val senderId = json["senderId"] as String
            val cipher = json["cipher"] as String
            val identityKeyId = (json["identityKeyId"] as Number).toInt()

            val result = secureProtocol.decryptStickyKey(senderId, cipher, identityKeyId)
            call.respond(mapOf("status" to "success", "key" to result))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun swapIdentityKey(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val keyId = (json["keyId"] as Number).toInt()

            val success = secureProtocol.swapIdentityKey(keyId)
            call.respond(mapOf("status" to "success", "result" to success))
        } catch (e: Exception) {
            call.respond(mapOf("status" to "error", "message" to e.message))
        }
    }

    suspend fun encryptFile(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val senderId = json["senderId"] as String
            val stickId = json["stickId"] as String
            val filePath = json["filePath"] as String
            val isSticky = json["isSticky"] as? Boolean ?: false

            val result = secureProtocol.encryptFile(senderId, stickId, filePath, isSticky)
            call.respondText(result.toString(), ContentType.Application.Json)
        } catch (e: Exception) {
            call.respond(
                HttpStatusCode.BadRequest,
                mapOf("status" to "error", "message" to e.message)
            )
        }
    }


    suspend fun decryptFile(
        secureProtocol: SecureProtocol,
        call: ApplicationCall
    ) {
        try {
            val json = call.receive<Map<String, Any>>()
            val senderId = json["senderId"] as String
            val stickId = json["stickId"] as String
            val filePath = json["filePath"] as String
            val cipher = json["cipher"] as String
            val outputPath = json["outputPath"] as String
            val isSticky = json["isSticky"] as? Boolean ?: false

            val result = secureProtocol.decryptFile(
                senderId,
                stickId,
                filePath,
                cipher,
                outputPath,
                isSticky
            )
            call.respond(mapOf("status" to "success", "decryptedPath" to result))
        } catch (e: Exception) {
            call.respond(
                HttpStatusCode.BadRequest,
                mapOf("status" to "error", "message" to e.message)
            )
        }
    }


}